from operator import index
from turtle import pos
import numpy as np
import math as m
from pyparsing import line


class GRA:
    def __init__(self, DecisionMatrix, weight, q, p=0.5):
        """
        假设只有效益型
        :param DecisionMatrix: 决策矩阵
        :param wight: 属性权重
        :param q: q值
        """
        self.matrix = DecisionMatrix
        self.weight = weight
        self.q = q
        self.p = p

    def addition(self, Ivq1, Ivq2, q):
        '''
        加法
        :param Ivq1:区间值
        :param Ivq2:区间值
        :param q:q值
        :return:
        '''
        a, b, c, d = Ivq1[0][0], Ivq1[0][1], Ivq1[1][0], Ivq1[1][1]
        e, f, g, h = Ivq2[0][0], Ivq2[0][1], Ivq2[1][0], Ivq2[1][1]
        u1 = a**q + e**q - (a**q) * (e**q)
        u2 = b**q + f**q - (b**q) * (f**q)
        v1 = c * g
        v2 = h * d
        return ([u1, u2], [v1, v2])

    def subtraction(self, Ivq1, Ivq2, q):
        '''
        减法
        :param Ivq1:区间值
        :param Ivq2:区间值
        :param q:q值
        :return:
        '''
        a, b, c, d = Ivq1[0][0], Ivq1[0][1], Ivq1[1][0], Ivq1[1][1]
        e, f, g, h = Ivq2[0][0], Ivq2[0][1], Ivq2[1][0], Ivq2[1][1]
        u1 = a * g
        u2 = b * h
        v1 = (c**q + e**q - (c**q) * (e**q))**(1 / q)
        v2 = (d**q + f**q - (d**q) * (f**q))**(1 / q)
        return ([u1, u2], [v1, v2])

    def multiplication(self, Ivq1, Ivq2, q):
        '''
        乘法
        :param Ivq1:区间值
        :param Ivq2:区间值
        :param q:q值
        :return:
        '''
        a, b, c, d = Ivq1[0][0], Ivq1[0][1], Ivq1[1][0], Ivq1[1][1]
        e, f, g, h = Ivq2[0][0], Ivq2[0][1], Ivq2[1][0], Ivq2[1][1]
        u1 = a * e
        u2 = b * f
        v1 = (c**q + g**q - (c**q) * (g**q))**(1 / q)
        v2 = (d**q + h**q - (d**q) * (h**q))**(1 / q)
        return ([u1, u2], [v1, v2])

    def division(self, Ivq1, Ivq2, q):
        '''
        除法
        :param Ivq1:区间值
        :param Ivq2:区间值
        :param q:q值
        :return:
        '''
        a, b, c, d = Ivq1[0][0], Ivq1[0][1], Ivq1[1][0], Ivq1[1][1]
        e, f, g, h = Ivq2[0][0], Ivq2[0][1], Ivq2[1][0], Ivq2[1][1]
        u1 = (a**q + g**q - (a**q) * (g**q))**(1 / q)
        u2 = (b**q + h**q - (b**q) * (h**q))**(1 / q)
        v1 = c * e
        v2 = d * f
        return ([u1, u2], [v1, v2])

    def getscore(self, IVq):
        '''
        我们提出的得分函数
        :param IVq:区间值
        :param q:q值
        :return:
        '''
        a = IVq[0][0]
        b = IVq[0][1]
        c = IVq[1][0]
        d = IVq[1][1]
        return (m.log(a + b + c + d + 1) + (b - a) + (d - c)**2 +
                (a - c + b - d) * m.log(3) / 2) / (2 * m.log(3))

    def Exponentiation(self, Ivq1, q, n):
        '''
        次方运算
        :param Ivq1:
        :param q:
        :param n:开多少次方
        :return:
        '''
        a, b, c, d = Ivq1[0][0], Ivq1[0][1], Ivq1[1][0], Ivq1[1][1]
        u1 = a**n
        u2 = b**n
        v1 = (1 - ((1 - (c**q))**n))**(1 / q)
        v2 = (1 - ((1 - (d**q))**n))**(1 / q)
        return ([u1, u2], [v1, v2])

    def getPosNegPoint(self, NormalizedMatrix):
        '''
        求正负理想解
        :param NormalizedMatrix: 区间值矩阵
        :param q: q值
        :return: 传入区间值矩阵的贴近度矩阵
        '''
        Row, Column = len(NormalizedMatrix), len(NormalizedMatrix[0])
        for i in range(Row):
            Positiveline, Negativeline = [], []
            for j in range(Column):
                ##求理想点
                Positiveidu1 = np.max((((NormalizedMatrix[:, j])[:, 0])[:, 0]))
                Positiveidu2 = np.max((((NormalizedMatrix[:, j])[:, 0])[:, 1]))
                Positiveidv1 = np.min((((NormalizedMatrix[:, j])[:, 1])[:, 0]))
                Positiveidv2 = np.min((((NormalizedMatrix[:, j])[:, 1])[:, 1]))
                Negativeidu1 = np.min((((NormalizedMatrix[:, j])[:, 0])[:, 0]))
                Negativeidu2 = np.min((((NormalizedMatrix[:, j])[:, 0])[:, 1]))
                Negativeidv1 = np.max((((NormalizedMatrix[:, j])[:, 1])[:, 0]))
                Negativeidv2 = np.max((((NormalizedMatrix[:, j])[:, 1])[:, 1]))
                Positiveline.append(
                    ([Positiveidu1,
                      Positiveidu2], [Positiveidv1, Positiveidv2]))
                Negativeline.append(
                    ([Negativeidu1,
                      Negativeidu2], [Negativeidv1, Negativeidv2]))
        return Positiveline, Negativeline

    def getDist(self, Ivq1, Ivq2, q):
        '''
        求解区间值广义正交模糊数Ivq1, Ivq2之间的距离
        :param Ivq1: 区间值
        :param Ivq2: 区间值
        :param q: q值
        '''
        a, b, c, d = Ivq1[0][0], Ivq1[0][1], Ivq1[1][0], Ivq1[1][1]
        e, f, g, h = Ivq2[0][0], Ivq2[0][1], Ivq2[1][0], Ivq2[1][1]
        D = 1 / 4 * (abs(a**q - e**q - c**q + g**q) +
                     abs(b**q - f**q - d**q + h**q))
        return D

    def getPosNegGRC(self, NormalizedMatrix, Positiveline, Negativeline, q):
        '''
        求解每一个评估指标在PIS和NIS条件下的GRC
        :param NormalizedMatrix: 区间值矩阵
        :param Positiveline: 正理想解
        :param Negativeline: 负理想解
        :param q: q值
        '''
        Row, Column = len(NormalizedMatrix), len(NormalizedMatrix[0])
        # 初始化矩阵
        posGRCMatrix, negGRCMatrix = [], []
        # TODO: 后续优化代码
        dPosMatrix, dNegMatrix = [], []
        for i in range(Row):
            line1, line2 = [], []
            for j in range(Column):
                line1.append(
                    self.getDist(NormalizedMatrix[i][j], Positiveline[j], q))
                line2.append(
                    self.getDist(NormalizedMatrix[i][j], Negativeline[j], q))
            dPosMatrix.append(line1)
            dNegMatrix.append(line2)
        maxPos, minPos = np.max(dPosMatrix), np.min(dPosMatrix)
        maxNeg, minNeg = np.max(dNegMatrix), np.min(dNegMatrix)
        for i in range(Row):
            line3, line4 = [], []
            for j in range(Column):
                line3.append((minPos + self.p * maxPos) /
                             (dPosMatrix[i][j] + self.p * maxPos))
                line4.append((minNeg + self.p * maxNeg) /
                             (dNegMatrix[i][j] + self.p * maxNeg))
            posGRCMatrix.append(line3)
            negGRCMatrix.append(line4)
        return (posGRCMatrix, negGRCMatrix)

    def getPosNegGRCLine(self, posGRCMatrix, negGRCMatrix, weight):
        '''
        获取每一行（每一个备选方案）的灰色关联系数
        :param posGRCMatrix: 每个备选方案的正理想解
        :param negGRCMatrix: 每个备选方案的负理想解
        :param weight: 权重
        '''
        Row, Column = len(posGRCMatrix), len(posGRCMatrix[0])
        posGRCLine, negGRCLine = [], []
        for i in range(Row):
            sumPos = sumNeg = 0
            for j in range(Column):
                sumPos += weight[j] * posGRCMatrix[i][j]
                sumNeg += weight[j] * negGRCMatrix[i][j]
            posGRCLine.append(sumPos)
            negGRCLine.append(sumNeg)

        return (posGRCLine, negGRCLine)

    def getRRDLine(self, posGRCLine, negGRCLine):
        '''
        计算每个备选方案的相对关联度，其中，相对关联度最大的方案为最优方案
        :param posGRCLine: 正理想解中，每一行（每一个备选方案）的灰色关联系数
        :param negGRCLine: 负理想解中，每一行（每一个备选方案）的灰色关联系数
        '''
        length = len(posGRCLine)
        RRDLine = []
        for i in range(length):
            RRDLine.append(posGRCLine[i] / (posGRCLine[i] + negGRCLine[i]))

        return RRDLine

    def getPosNor(self, Ivq, Pos, Neg, q):
        '''
        对正类型的指标进行处理
        :param Ivq: 区间值
        :param Pos: 正理想解元素
        :param Neg: 负理想解元素
        :param q: q值
        :return:
        '''
        PosNor = self.division(self.subtraction(Ivq, Neg, q),
                               self.subtraction(Pos, Neg, q), q)
        return PosNor

    def getNegNor(self, Ivq, Pos, Neg, q):
        '''
        对负类型指标进行处理
        题中用的是+1，本处改成+正理想元素
        :param Ivq:区间值
        :param Pos: 正理想解元素
        :param Neg: 负理想解元素
        :param q: q值
        :return:
        '''

        t = self.division(self.subtraction(Neg, Ivq, q),
                          self.subtraction(Pos, Neg, q), q)
        NegNor = self.addition(t, Pos, q)
        return NegNor

    def NormalizedMatrix(self, Matrix, Positiveline, Negativeline, q):
        '''
        规范化矩阵
        :param Matrix:决策矩阵
        :param q:q值
        :return:Matrix规范化之后的矩阵
        '''
        costindex = (0, 1)  # 表示哪一列是负指标,从0开始
        for i in range(len(Matrix)):
            for j in costindex:
                Matrix[i][j] = self.getNegNor(Matrix[i][j], Positiveline[j],
                                              Negativeline[j], q)
            else:
                Matrix[i][j] = self.getPosNor(Matrix[i][j], Positiveline[j],
                                              Negativeline[j], q)
        return Matrix

    def getMarginalUtilityScore(self, NormalizedMatrix):
        '''
        获取边际效用得分
        :param NormalizedMatrix:规范化矩阵
        :return:
        '''
        ScoreMatrix = []
        for Row in range(len(NormalizedMatrix)):
            Scoreline = []
            for Column in range(len(NormalizedMatrix[0])):
                t = self.getscore(NormalizedMatrix[Row][Column])
                Scoreline.append(t)
            ScoreMatrix.append(Scoreline)
        return ScoreMatrix

    def getFinalUtilityScore(self, ScoreMatrix, Weight):
        '''
        获取最终效用得分
        :param ScoreMatrix: 得分矩阵
        :param Weight: 权重
        :return:
        '''
        U = []
        for Row in range(len(ScoreMatrix)):
            u0 = 0
            for Column in range(len(ScoreMatrix[0])):
                u0 += Weight[Column] * ScoreMatrix[Row][Column]
            U.append(u0)
        return U

    def getResult(self):
        '''
        最终结果
        :param Matrix:
        :param q:
        :return:
        '''
        # step1: get PIS, NIS and NormalizedMatrix
        Positiveline, Negativeline = self.getPosNegPoint(self.matrix)
        NormalizedMatrix = self.NormalizedMatrix(Matrix, Positiveline,
                                                 Negativeline, self.q)
        # step2: Calculate the grey relational coefficient of each evaluation value from PIS and NIS
        posGRCMatrix, negGRCMatrix = self.getPosNegGRC(NormalizedMatrix,
                                                       Positiveline,
                                                       Negativeline, self.q)
        # step3: Compute the degree of grey relational coeffi-cient of each alternatives from PIS and NIS
        posGRCLine, negGRCLine = self.getPosNegGRCLine(posGRCMatrix,
                                                       negGRCMatrix,
                                                       self.weight)
        # step4: get the relative relational degree of each alternative,
        # the maxmum is the best
        RRDLine = self.getRRDLine(posGRCLine, negGRCLine)
        max_value = max(RRDLine)
        max_index = RRDLine.index(max_value)
        RRDLine=[i/sum(RRDLine) for i in RRDLine]
        return RRDLine
        # print(
        #     f'the relative relational degree of each alternative is:{np.around(RRDLine, 4)}\nthe best choice of these alternatives is:{max_index + 1}'
        # )
        # step5: get the weight of vector
        # step6: repeat the step3 to step5, get the maxmum is the best choice


if __name__ == "__main__":
    Matrix = np.array([[([0.58, 0.75], [0.1, 0.2]), ([0.7, 0.78], [0.2, 0.3]),
                        ([0.5, 0.78], [0.1, 0.2]), ([0.65, 0.7], [0.3, 0.35]),
                        ([0.4, 0.5], [0.5, 0.6])],
                       [([0.5, 0.6], [0.1, 0.2]), ([0.75, 0.78], [0.2, 0.3]),
                        ([0.6, 0.78], [0.1, 0.15]), ([0.7, 0.8], [0.2, 0.3]),
                        ([0.5, 0.6], [0.4, 0.5])],
                       [([0.5, 0.78], [0.1, 0.2]), ([0.6, 0.7], [0.3, 0.4]),
                        ([0.7, 0.78], [0.2, 0.3]), ([0.6, 0.7], [0.3, 0.4]),
                        ([0.35, 0.45], [0.5, 0.65])],
                       [([0.75, 0.780], [0.2, 0.3]), ([0.6, 0.75], [0.2, 0.3]),
                        ([0.7, 0.78], [0.2, 0.3]), ([0.55, 0.65], [0.3, 0.4]),
                        ([0.35, 0.4], [0.6, 0.65])],
                       [([0.6, 0.75], [0.3, 0.4]), ([0.5, 0.6], [0.4, 0.5]),
                        ([0.6, 0.7], [0.3, 0.4]), ([0.5, 0.6], [0.4, 0.5]),
                        ([0.1, 0.2], [0.4, 0.5])]])
    weight = (0.2, 0.2, 0.2, 0.2, 0.2)

    gra = GRA(Matrix, weight, 3)
    result = gra.getResult()
    print(result)